

var Ant = global.Ant = function(selector, context) {
	// The Ant object is actually just the init constructor 'enhanced'
	return new Ant.prototype.init(selector, context);
};

// Map over the $ in case of overwrite
if (global.$)
	var _$ = global.$;

// Map the Ant namespace to the '$' one
global.$ = Ant;

Ant.prototype = {
    /**
     * Process user resources selector from hash-form
     * @argument {Object| Selector} the resources selector
     */
	init : function(selector) {
        var rs = this.group(selector);
         this._resources = rs.size() >0? rs: null;
        Ant.dumpResources(this._resources);
        return this;
	},
    
    /**
     * Group a set of resource together by resource collection
     * @param {Object} selector
     * @return {ResourceCollection}
     */
    group: function (selector) {
        
        var t =  Ant.fn.type(selector), rs = Ant.resources(), self = arguments.callee;
        if(t === 'object')  
        {
            $H(selector).each(function (item) {
                rs.add(Ant[item.key](item.value));
            });
        }
        else if(t === 'array')
        {
            selector.inject(rs, function (item) {
                rs.add(self(item));
                return rs;
            })
        }
        
      
        return rs;
    },

	// The current version of Ant being used
	Ant : "0.1",
    
    
	// The collection of resources currently being selected
	_resources : null,
    /**
     * Decide the resource type
     * @param {*} res
     * @return {String} resource type presentation in string
     */
    type: function (resource) {
        var res = resource, t = "";
        if(res instanceof java.io.File)
            t = res.exists() ?(res.isFile()?"file":"dir"):'empty';
        else if(res instanceof java.net.URL)
            t = 'url';
        else if (res instanceof org.apache.tools.ant.types.ResourceCollection)
            t = 'resource';
        else
            t = $T(resource);
        return t;
    }

};

Ant.fn = Ant.prototype;
// Give the init function the Ant prototype for later instantiation
Ant.prototype.init.prototype = Ant.prototype;

Ant.extend = Ant.fn.extend = function() {
	// copy reference to target object
	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;

	// Handle a deep copy situation
	if (target.constructor == Boolean) {
		deep = target;
		target = arguments[1] || {};
		// skip the boolean and the target
		i = 2;
	}

	// Handle case when target is a string or something (possible in deep copy)
	if (typeof target != "object" && typeof target != "function")
		target = {};

	// extend Ant itself if only one argument is passed
	if (length == 1) {
		target = this;
		i = 0;
	}

	for (; i < length; i++)
		// Only deal with non-null/undefined values
		if ((options = arguments[i]) != null)
			// Extend the base object
			for (var name in options) {
				// Prevent never-ending loop
				if (target === options[name])
					continue;

				// Recurse if we're merging object values
				if (deep && options[name] && typeof options[name] == "object"
						&& target[name] && !options[name].nodeType)
					target[name] = Ant.extend(target[name], options[name]);

				// Don't bring in undefined values
				else if (options[name] != undefined)
					target[name] = options[name];

			}

	// Return the modified object
	return target;
};

Ant.extend({
    
     //The global exception object
    RESOURCE_TYPE_ERROR:  'Ant resource type mismatch',
    
	
    
	createTarget : function() {
		return new Packages.org.apache.tools.ant.Target();
	},

	/**
	 * Load new script file/directory(all js files) and execute it.
	 * 
	 * @param {java.io.File}
	 *            src script file
	 */
	load : function(srcFile) {

		print('[load] Execute scripts from files:\n');

		var f = new java.io.File(srcFile);
		if (f.isFile()) {
			print(f.getPath() + ".\n");
			var loadfile = project.createTask('loadfile');
			// loadfile.setEncoding('utf-8');
			loadfile.setProperty('ant.tmp');
			loadfile.setSrcFile(f);
			loadfile.perform();
			eval(project.getProperty('ant.tmp'));

		} else if (f.isDirectory()) {
			f.listFiles(new FilenameFilter({
						accept : function(dir, name) {
							name.match(/.js$/);
						}
					})).each(function(file) {
						load(file);
					});
		} else {
			print(f.getPath() + "Unable to locate any resources to load.\n");
		}

	},

	

	echo : function(msg, level) {
		var echo = project.createTask("echo");
		echo.setMessage(msg);
        var l = new org.apache.tools.ant.taskdefs.Echo.EchoLevel();
        l.setValue( l[(level||'info').toUpperCase()].toString());
        echo.setLevel(l);
		echo.perform();
	},
    
	
    
    /**
     * Convert the output of task from 'primitive' data type to appropriate resource type  
     * @param {java.io.File} resource
     * @return {org.apache.tools.ant.types.Resource} result
     */
    purify: function (source) {
        var t = Ant.type(source), result;
        switch(t)
        {
            case 'file':
                result = file(source);
                break
            case  'dir':
                result = fileset(source);
                break;
            case 'url':
                result = uri(source);
                break;
            default:
                result = source;
        }
        return result;
    },
    
    

	

	/**
	 * Iterator over the specified resource collection 
	 * @param {} resCollection
	 * @param {} fn - callback function 
	 */
	eachResources : function(resCollection, fn) {
		var iter = resCollection
				&& resCollection instanceof org.apache.tools.ant.types.ResourceCollection
				&& resCollection.iterator();
		while (iter && iter.hasNext()) {
            if(fn)
			 fn(iter.next());
		}
	},
    
    
    dumpResources: function (resCollection) {
        var resultMsg = '';
        Ant.eachResources(resCollection, function (res) {
            resultMsg += ('\t'+res + '\n');
        });
        Ant.echo('Current selected resources:\n'+resultMsg);
    }
    
});

(function() {
    //TODO: date types definition list, TBC...
    var dataTypeList = {
    'resource':{}, 'file':{}, 'url':{}, 'zipentry':{},'string':{}, 'propetyresource':{}, // built-in
																							// resources
																							// type
    'fileset':{
    fixture:['dir','file']
    }, 'dirset':{}, 'filelist':{},'path':{},'tarfileset':{},'zipfileset':{},'propertyset':{}, // "legacy"
																											// data
																											// type
    'files':{},'resources':{},'restrict':{},'sort':{},'first':{},'last':{},'tokens':{},'union':{},'intersect':{},'difference':{},// additional
																																	// built-in
																																	// resource
																																	// collections
    'mapper':{},// all types of file mappers
    'filterset':{},'filterchain':{},'headfilter':{},'escapeunicode':{},'linecontains':{},'linecontainsregexp':{},// content
																													// filters....
    'patternset':{},   // file name patterns
    'pathelement':{},'classpath':{},'path':{},   // path-like structures
    'redirector':{}, // External command I/O redirectors
    'contains':{},'date':{},'depend':{},'depth':{},'different':{},'filename':{},'present':{},'containsregexp':{},'size':{},'type':{},'modified':{}   // resources
																																					// selectors
    };
    
    
  
    var taskMap = {
        'copy' : {
            output : 'tofile',
            multiOutput : 'todir'
        },
        /*
         'wget' : {    // 'get' task doesn't support nested resources
            taskName: 'get',
            output : 'dest'
        },
        */
        'unzip' : {
            output : 'dest'
        },
        'zip' : {
            output : 'destFile'
        },
        'concat' : {
            output : 'destfile'
        }
    };
    
    $H(dataTypeList).each(function (hashItem) {
       Ant[hashItem.key] = 
       /**
		 * Wrapper function for creating each type of Ant data structures, auto
		 * delegate to <b>ant.Project#createDataType(java.lang.String dataType)</b>
		 * 
		 * @param {Object}
		 *            opt - Data type definition options
		 */
       function (opt) {
        
            var datatype = project.createDataType(hashItem.key);
           
            /**
			 * Delegate Ant datatype options to corresponding <b>ant.types.DataType</b>  as either attribute or nested data types 
			 * 
			 * @param pAttr
			 */
            function configDatatype(pDatatype, pOpt) {
                
                var field;
				var setter, hasAttr, attrN, attrV;
				for (field in pDatatype) {
					if ((typeof pDatatype[field]) === 'function') {
                        
						setter = (field.startsWith('set') && 'set') // attributes
								|| (field.startsWith('add') && 'add'); // nested
																			// data
																			// types
						if (setter) {

							attrN = field.replace(new RegExp('^' + setter),
									'').toLowerCase();

							hasAttr = (attrN in pOpt);

							if (hasAttr) {
								attrV = pOpt[attrN];
								 Ant.echo('Setting attr:'+attrN+" by method:"+field);
								if (setter === 'set')
									pDatatype[field](attrV); // set
																// attributes
								else if (setter === 'add')
									pDatatype[field] // set nested data types
									(Ant[attrN](attrV)); // create nested
															// data types
							}
						}
					}
				}

			};
             
            if(opt) // process configuration values
            {
                var fixtures;   
				fixtures = hashItem.value.fixture;  // @see dataTypeList
				if (fixtures) {
                    fixtures = $H(opt).extract(fixtures);
				}
                

				configDatatype(datatype, fixtures); // mandatory
													// attributes first
													// to avoid "missing
													// major attribute"
													// exceptoin from
													// Ant.

		
				configDatatype(datatype, opt);    // other options
													// after
				}
                
            return datatype;
            }
            
    });

    // Ant task wrapper generation,
    $H(taskMap).each(function(item) {

        Ant[item.key] =
        /**
		 * Wrapper function for each Ant task, auto delegate to
		 * <b>ant.Project#createTask(java.lang.String taskType)</b>
		 * 
		 * With all task attributes defined in param - opt, while input of
		 * task(ant.types.resources.Resources) goes into the param - resources.
         * 
		 * Output (normally destination attribute in Ant)of each task were
		 * delegate to the return value of this function;The actual attribute
		 * of Ant tasks which has more than one output will be determined by it's input mutiplicity.
		 * 
		 * @param {ant.types.resources.Resources}
		 *            resources - the resources which every ant task act upon 
		 * @param {Object}
		 *            opt - the config options for this task
		 * @return {}  - the output resource of this task if it has
		 */
        function antTaskWrapper(resources, opt) {

			var task = project.createTask(item.value.taskName || item.key);

			// feed resources as input

			// Ant.dumpResources(resources);//DEBUG:
            print(task.getTaskName());
			task.add(resources);

			// feed options as attributes
			// Ant.echo(Object.toJSON(opt));//DEBUG:
			$H(task).each(function(attr) {
				var attr = attr.key, setter;
				if ((typeof task[attr] === 'function')
						&& attr.startsWith('set')
						&& (setter = attr.replace(/^set/, '').toLowerCase()) in opt) {
					task[attr](opt[setter]);
					// Ant.echo(attr+":"+opt[setter]);//DEBUG
				}
			});

			task.perform();

			return
            // task output resources as return value, the outputed attribute depends on resources multiplicity
            opt[resources.size() > 1 ? item.output : item.multiOutput];
		};
    
        Ant.fn[item.key] =
        
        /**
		 * Another layer of task wrapper for the purpose of making the chain
		 * operations pattern happens, allowing codes for sequencial tasks which depend on output of previous ones 
         * can be organized narratively.
		 * 
		 * @param {Object}
		 *            taskOpt
		 */
        function antTaskChainWrapper(taskOpt) {
            var result = Ant[item.key](this._resources, taskOpt||{});
            this._resources =  Ant.purify(result);
            return this;    //chain continues
        }
        
    })
})();


